DYNAMIC UPDATE OF CLASSIC DESK ACCESSORIES
by Richard Bennett
Copyright (c) 1990 Apple Users' Group, Sydney
Republished from Applecations, a publication of the Apple Users' Group, Sydney, Australia.
If you've ever written a CDA, you'll know how frustrating it is to reboot after each assembly, just to test the darn thing. If the CDA gets fairly big, development starts to slow down quite considerably. What is needed, is a way to re-install CDAs dynamically. Also, considering that it's a CDA, you could then test, debug, and re-assemble the entire thing without ever leaving the assembler.
There are currently NDAs available to re-install NDAs, but nothing for CDAs. With the release of System 5.0 however, the job of dynamic re-installs becomes fairly easy.
First off, we have to consider a few posing questions;
1 - How do we invoke the re-install?
2 - What tools and environment are necessary for the re-install?
3 - How do we perform the re-install?
The first is easy. Simply install an option into the CDA to re-install itself. When that option is selected, the CDA should reload itself from disk, install it into the CDA menu, and remove the current entry.
For the second, we'll obviously require GS/OS, and the Desk Manager to perform the CDA manipulations. We may also need the Memory Manager, and obviously the System Loader.
How do we do it? There are a few problems here. The first, is that we have no idea who we are, pathname wise. This is where the System Loader comes in handy, as the tool call _LGetPathname2 will return a GS/OS pathname when supplied with the UserID (_LGetPathname will also do, but will only return a class 0 string pathname). This is great, but how do we get the UserID? The original UserID that the System Loader used to load the CDA, is unknown to the CDA at the moment, but the easiest way to get it is as follows;
FindMyUserID PHA ;Push some space
PHA
PushLong #FindMyUserID ;Push my address on the stack
_FindHandle ;Find my handle (allocated by the
; System Loader on bootup)
PullLong 0 ;Retrieve my handle
LDY #6 ;Offset in HandleRec of UserID
LDA [0],Y ;Get my UserID
STA MyID
RTS
This is necessary, as all memory allocations the CDA does, are allocated by the UserID obtained from the startup. eg. If your CDA requires a UserID to allocate memory, you should do the following;
GetNewUserID PHA ;Push some space
_MMStartup ;Get a new Master ID
PLA ;Retrieve it
STA UserID ;Save it
ORA #$100 ;Make a new Aux ID
STA AllocID ;Use it for allocating memory
RTS
Now, we've got the UserID, all we need now is the pathname;
FindMyPath PHA ;Some space
PHA
PEI MyID ;First parameter is UserID to find
PEA 0 ;Next is the segment number
_LGetPathname2
PullLong PathAdr ;Retrieve the address of the path
RTS
Ok, now to load the file. To do this, we call _InitialLoad2, which loads the file and returns with it's address, which we can find the handle for, and use it to install it in the CDA menu by using _InstallCDA.
The next step, is to discard myself. To start off, we should dispose of any memory we've allocated privately. Now, the hard part is to dispose of myself, delete my UserID, discard my entry in the System Loader pathname table, and either return to the CDA menu, or call the new CDA directly (we have the pointer to it remember!). The call to do all this, is _UserShutdown.
The Memory Manager won't move or purge any memory if the system is currently running under an interrupt request. This includes the Desk Accessory menu. So theoretically, we can simply dispose of ourself, and THEN either return, or call the new CDA. Unfortunately, it isn't that simple. If, in future versions of the system software, the _UserShutdown call decides to allocate any memory (for work areas such as re-building the pathname table, or the Memory Manager re-building the UserID list), and the CDA memory was already purged, and memory is almost full, the allocations it makes could overwrite where I was calling from. When the System Loader returns, I may not necessarily be still there! Also, executing code from a purged memory segment isn't very clever programming. A way around this, is to call _UserShutdown from somewhere in bank 0 (say at 00/0200) where the Memory Manager can't touch you, or simply play the odds (like I do) that the above will never happen (after all, it's only a debugging feature that'll be removed when you've finished writing it!).
So, to summarize, these are the steps to follow;
1 - Get the UserID that the CDA was loaded with (_FindHandle).
2 - Get my pathname from the System Loader (_LGetpathname2).
3 - Call _InitialLoad2 to load myself from disk.
4 - Install it into the CDA menu (_InstallCDA).
5 - Remove myself from the CDA menu (_RemoveCDA).
6 - Dispose all of the memory that I've allocated privately.
7 - Call _UserShutdown to dispose of everything, and optionally call the new CDA.
The entire code (in Merlin format, WITHOUT supermacros) follows;
ReloadCDA PHA ;Find me
PHA
PushLong #*
_FindHandle
PullLong MyHandle
LDY #6
LDA [MyHandle],Y
STA MyID
PHA ;Push space for _InitialLoad2
PHA
PHA
PHA
PHA
PEA $5000 ;UserID type for loaded file
PHA ;Push space and UserID for _LGet
PHA
PHA
PEA 0 ;Segment number
_LGetPathname2 ;Leave results on stack
PEA $FFFF ;Don't use special memory
PEA 1 ;Loader type 1
_InitialLoad2 ;Load the CDA
PLA ;Get the UserID assigned
_FindHandle ;Address and DP/S on stack still
_InstallCDA ;Install the CDA
PushLong MyHandle ;Now remove myself from the menu
_RemoveCDA
PHA ;Space for _UserShutdown
PEI MyID ;Shutdown who?
PEA 0 ;Kill everything!
_UserShutdown
PLA ;Remove the UserID
RTL ;Return to Desk Manager
;(normal CDA quit)
Here is the list of tool calls used;
_FindHandle $1A03 Memory Manager
_LGetPathname2 $2211 System Loader
_InitialLoad2 $2011 System Loader
_InstallCDA $0F05 Desk Manager
_RemoveCDA $2105 Desk Manager
_UserShutdown $1211 System Loader
Permission is hereby granted for non-profit user groups to republish this content. PLEASE CREDIT THE AUTHOR AND THE SOURCE: Applecations, publication of the Apple Users' Group, Sydney, Australia